#include "cpp2json_internal.hpp"

#include <map>

#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <rapidjson/pointer.h>

namespace tb {

namespace serializer {

namespace internal {

static struct __Cpp2JsonSorage {
    std::map<std::string, J2OFn> fnJ2O;
    std::map<std::string, O2JFn> fnO2J;
    std::map<std::string, Consturctor> fnCtor;
    std::map<std::string, Desturctor> fnDtor;
} a__Cpp2JsonSorage;

CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(bool, bool)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(char, char)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(unsigned char, unsigned_char)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(short, short)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(unsigned short, unsigned_short)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(int, int)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(unsigned int, unsigned_int)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(long, long)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(unsigned long, unsigned_long)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(long long, long long)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(unsigned long long, unsigned_long_long)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(double, double)
CPP2JSON_DEFINE_SIMPLE_TYPE_GLOBAL_INITIELIZER(float, float)

std::string NormalizeTypeName(const std::string &dotTypeName) {
    std::string r;
    for (std::string::const_iterator c = dotTypeName.begin();
        it != dotTypeName.end(); ++it) {
        if (*c == '.') {
            r += "::";
        } else {
            r += *c;
        }
    }
    return r;
}

bool JsonTypeName(const char *jsonData, std::string &typeName) {
    rapidjson::Document doc;
    rapidjson::StringStream s(jsonData);

    doc.ParseStream(s);
    if (doc.HasParseError()) {
        return false;
    }

    const rapidjson::Document::ValueType *typeVal = rapidjson::GetValueByPointer(doc, "/_$schema");
    if (typeVal && typeVal->IsString()) {
        typeName = NormalizeTypeName(typeVal->GetString());
        return true;
    }
    return false;
}

void *CreateObject(const char *typeName) {
    if (Consturctor fn = a__Cpp2JsonSorage.fnCtor[typeName]) {
        return fn();
    }
    assert(false);
    return NULL;
}

void DestroyObject(const char *typeName, void *d) {
    if (Desturctor fn = a__Cpp2JsonSorage.fnCtor[typeName]) {
        fn(d);
    }
    assert(false);
}

bool Json2Object(
    const char *typeName, 
    const rapidjson::Value *val, 
    void *data, 
    std::string &err) {
    if (J2OFn fn = a__Cpp2JsonSorage.fnJ2O[typeName]) {
        return fn(val, data, err);
    } 
    err.clear();
    err += "can not handle type " + typeName;
    return false;
}

bool Object2Json(
    const char *typeName, 
    cosnt void *data, 
    rapidjson::Value *val, 
    rapidjson:Document::AllocatorType &allocator, 
    std::string &err) {
    if (O2JFn fn = a__Cpp2JsonSorage.fnO2J[typeName]) {
        return fn(data, val, allocator, err);
    } 
    err.clear();
    err += "can not handle type " + typeName;
    return false; 
}

void Stringify(rapidjson::Document &doc, std::string &err) {
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    doc.Accept(writer);
    str = buffer.GetString();
}

void RegisterConstructor(const char *typeName, Consturctor fn) {
    a__Cpp2JsonSorage.fnCtor[typeName] = fn;
}

void RegisterDestructor(const char *typeName, Desturctor fn) {
    a__Cpp2JsonSorage.fnDtor[typeName] = fn;
}

void RegisterJ2OFn(const char *typeName, J2OFn fn) {
    a__Cpp2JsonSorage.fnJ2O[typeName] = fn;
}

void RegisterO2JFn(const char *typeName, O2JFn fn) {
    a__Cpp2JsonSorage.fnO2J[typeName] = fn;
}

} // end namsepsace internal

} // end namespace serializer

} // end namespace tb